07 类与面向对象
面向对象编程使用类组织数据和行为。Python 的类机制支持实例变量、类变量、继承、多重继承、名称改写、迭代器和生成器等特性。
一、定义类
1.1 基本语法
python
>>> class MyClass:
... """一个简单的类"""
... i = 12345 # 类变量
...
... def f(self): # 方法
... return 'hello world'
...
>>> x = MyClass()
>>> x.f()
'hello world'self表示实例本身。通过实例调用方法时,self会由 Python 自动传入。
1.2 __init__构造方法
python
>>> class Complex:
... def __init__(self, realpart, imagpart):
... self.r = realpart # 实例变量
... self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r
3.0
>>> x.i
-4.51.3 实例变量和类变量
python
>>> class Dog:
... kind = 'canine' # 类变量,所有实例共享
...
... def __init__(self, name):
... self.name = name # 实例变量,每个实例独立
...
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind
'canine'
>>> d.name
'Fido'
>>> d.kind is e.kind
True类变量由所有实例共享,实例变量属于单个实例。将列表、字典等可变对象作为类变量时需要注意共享状态:
python
>>> class Dog:
... tricks = [] # 错误!所有实例共享同一个列表
...
... def __init__(self, name):
... self.name = name
...
... def add_trick(self, trick):
... self.tricks.append(trick)可变对象通常应在__init__中初始化:
python
>>> class Dog:
... def __init__(self, name):
... self.name = name
... self.tricks = [] # 每个实例独立的列表
...
... def add_trick(self, trick):
... self.tricks.append(trick)二、继承
2.1 基本继承
python
>>> class Animal:
... def speak(self):
... return "..."
...
... class Dog(Animal):
... def speak(self):
... return "汪汪"
...
>>> d = Dog()
>>> d.speak()
'汪汪'2.2 isinstance()和issubclass()
python
>>> isinstance(d, Dog)
True
>>> isinstance(d, Animal)
True
>>> issubclass(Dog, Animal)
True2.3 多重继承
python
>>> class A:
... def hello(self):
... print("A")
...
>>> class B(A):
... def hello(self):
... print("B")
...
>>> class C(A):
... def hello(self):
... print("C")
...
>>> class D(B, C):
... pass
...
>>> d = D()
>>> d.hello()
BPython 使用 C3 线性化算法解析多重继承顺序,可以通过D.__mro__查看方法解析顺序。
三、私有变量
3.1 约定和名称改写
python
>>> class Mapping:
... def __init__(self, iterable):
... self.items_list = []
... self.__update(iterable)
...
... def update(self, iterable):
... for item in iterable:
... self.items_list.append(item)
...
... __update = update # 私有副本
...
>>> class MappingSubclass(Mapping):
... def update(self, keys, values):
... # 不会覆盖__update
... for item in zip(keys, values):
... self.items_list.append(item)__开头的名称会被改写为_类名__名称,用于降低与子类名称冲突的概率。这不是真正的私有机制,而是名称改写。
3.2 约定
_开头:受保护约定,外部可以访问但不建议依赖__开头:私有的,名称会被改写- 没有前缀:公开的
四、迭代器
4.1 迭代协议
python
>>> class Reverse:
... def __init__(self, data):
... self.data = data
... self.index = len(data)
...
... def __iter__(self):
... return self
...
... def __next__(self):
... if self.index == 0:
... raise StopIteration
... self.index -= 1
... return self.data[self.index]
...
>>> for char in Reverse('spam'):
... print(char, end=' ')
...
m a p s实现__iter__()和__next__()的对象就是迭代器。for循环会自动调用这两个方法。
4.2 StopIteration
__next__()在没有更多元素时抛出StopIteration,for循环会捕获该异常并结束循环。
五、生成器
5.1 基本用法
生成器是一种创建迭代器的简洁方式:
python
>>> def reverse(data):
... for index in range(len(data) - 1, -1, -1):
... yield data[index]
...
>>> for char in reverse('spam'):
... print(char, end=' ')
...
m a p syield会使函数成为生成器函数。每次调用next()时,函数会执行到下一个yield位置并暂停。
5.2 生成器表达式
python
>>> sum(i*i for i in range(10)) # 求平方和
285
>>> list(i*i for i in range(10)) # 转成列表
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> unique_words = set(word for line in page for word in line.split())生成器表达式使用圆括号,按需生成元素,不会一次性创建完整列表,因此更适合大数据量场景。
六、魔术方法
6.1 常用魔术方法
python
>>> class Vector:
... def __init__(self, x, y):
... self.x = x
... self.y = y
...
... def __repr__(self):
... return f'Vector({self.x!r}, {self.y!r})'
...
... def __abs__(self):
... return (self.x ** 2 + self.y ** 2) ** 0.5
...
... def __add__(self, other):
... return Vector(self.x + other.x, self.y + other.y)
...
... def __mul__(self, scalar):
... return Vector(self.x * scalar, self.y * scalar)
...
... def __bool__(self):
... return bool(abs(self))
...
>>> v = Vector(3, 4)
>>> v
Vector(3, 4)
>>> abs(v)
5.0
>>> v + Vector(1, 2)
Vector(4, 6)
>>> v * 3
Vector(9, 12)6.2 常见魔术方法列表
| 方法 | 说明 |
|---|---|
__init__ | 构造方法 |
__repr__ | 字符串表示(给程序员看) |
__str__ | 字符串表示(给用户看) |
__len__ | len() |
__getitem__ | 索引访问 |
__setitem__ | 索引赋值 |
__contains__ | in运算符 |
__add__ | + |
__mul__ | * |
__eq__ | == |
__lt__ | < |
__bool__ | 布尔值 |
__iter__ | 迭代器 |
__next__ | 获取下一个元素 |
__enter__/__exit__ | with语句 |
__call__ | 函数调用 |
七、总结
| 概念 | 说明 |
|---|---|
| 类变量 | 所有实例共享 |
| 实例变量 | 每个实例独立 |
| 继承 | 子类继承父类 |
| 多重继承 | C3线性化解析顺序 |
__私有 | 名称改写,不是真私有 |
| 迭代器 | __iter__() + __next__() |
| 生成器 | yield关键字 |
| 生成器表达式 | 圆括号,按需生成 |
类变量适合保存不可变共享数据;可变数据通常应在__init__中初始化。生成器按需生成元素,适合处理大数据量或流式数据。